use std::{collections::HashMap, convert::TryFrom, net::{SocketAddr, ToSocketAddrs, UdpSocket}};

pub struct Server {
    socket: UdpSocket,
    recv_buf: Vec<u8>,
    users: HashMap<String, SocketAddr>,
}

impl Server {
    pub fn new() -> Self {
        let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
        socket.connect("www.baidu.com:80").unwrap();

        Self::from_addr(socket.local_addr().unwrap())
    }

    pub fn from_addr<A: ToSocketAddrs>(addr: A) -> Self {
        let socket = UdpSocket::bind(addr).unwrap();

        Self {
            socket,
            recv_buf: vec![0u8; 1024],
            users: HashMap::new(),
        }
    }

    pub fn get_addr(&self) -> SocketAddr {
        self.socket.local_addr().unwrap()
    }

    pub fn update(&mut self) {
        if let Ok((len, addr)) = self.socket.recv_from(&mut self.recv_buf) {
            if let Ok(pak) = moyu::Packet::try_from(&self.recv_buf[..len]) {
                println!("Package recv: {}[{:?}]: {}", pak.get_user_name(), pak.get_op(), pak.get_message());
                match pak.get_op() {
                    moyu::protocol::Operator::Request => {
                        match pak.get_message() {
                            "EXIT" => {
                                self.users.remove(pak.get_user_name());
                            }
                            _ => {
                                self.users.insert(pak.get_user_name().into(), addr);
                            }
                        }
                    }
                    moyu::protocol::Operator::Query => {
                        let mut msg = String::new();
                        for (name, _) in &self.users {
                            msg += &format!("{},", name);
                        }

                        let reply = moyu::Packet::new(
                            "Server",
                            moyu::protocol::Operator::Reply,
                            msg.as_str()
                        ).encode();

                        self.socket.send_to(&reply[..], &self.users[pak.get_user_name()]).unwrap();
                    }
                    moyu::protocol::Operator::SendMessage => {
                        let transmit = moyu::Packet::new(
                            pak.get_user_name(), 
                            moyu::protocol::Operator::TransmitMessage, 
                            pak.get_message()).encode();

                        for (_, addr) in &self.users {
                            self.socket.send_to(&transmit[..], addr).unwrap();
                        }
                    }
                    moyu::protocol::Operator::Tell => {
                        let mut split = pak.get_message().split(":");
                        let name = split.next().unwrap();

                        if let Some(msg) = split.next() {
                            let transmit = moyu::Packet::new(
                                pak.get_user_name(), 
                                moyu::protocol::Operator::TransmitMessage, 
                                msg).encode();

                            if let Some(addr) = self.users.get(name) {
                                self.socket.send_to(&transmit[..], addr).unwrap();
                            }
                        }
                    }
                    _ => {}
                }
            }
        }
    }
}